// OpenSCAD rendering of the PARTYLand REMAKE project.
// http://openscad.org/
// http://jucablues.blogspot.com/
//
// (c)2012 Felipe Sanches <fsanches@metamaquina.com.br>
// licensed under GPLv3 or later

playfield_version="msdos";

inch=25.4;//mm
m3_diameter=3;
playfield_thickness=15;
spacer_height=12;//TODO
acrylic_thickness=2;
//PUKE_height=67;//TODO
PUKE_height = spacer_height*3 + 2*acrylic_thickness;
left_tracks_height=52;//TODO
right_tracks_height=68-PUKE_height;
thickness=4;
links=false;
link_radius=21;
link_thickness=1;
link_width=8;
insert_thickness = 6;

use <../pinball/shooter.scad>;
use <../pinball/stud.scad>;
use <../pinball/post.scad>;
use <../pinball/flipper.scad>;
use <../pinball/popbumper.scad>;
use <../pinball/standup.scad>;
use <../pinball/lane-guide.scad>;
use <../pinball/ramp.scad>;
use <../pinball/rollover.scad>;
use <../pinball/slingshot.scad>;

module raise(z){
  translate([0,0,z])
  child(0);
}

launch_lane_x = 600;
ball_list = [
  [launch_lane_x,110],
  [launch_lane_x,310],
  [launch_lane_x,510],
  [launch_lane_x,610],
  [launch_lane_x,640],
  [launch_lane_x,680],
  [340,1010]
];

shooter_coords = [571,92];

lane_guide_coords = [
  [310, 1076],
  [366, 1066],
  [422, 1076],
];

puke_rollover_coords = [
  [280,1060],
  [338,1050],
  [394,1050],
  [451,1060]
];

rollover_coords = [
  [0, 74.5,360],
  [0, 37.4,320],
  [0, 520.4, 360],
  [0, 560.5, 320],
  [-120, 457,1129.5],
  [105, 269,1147.3]
];

wiring_hole_coords = [
//big hole for PUKE LEDs and Switches
  [255,1059],
  [255,1057],
  [255,1055],

//for arcade LEDs
  [262,930],

//for cyclone ramp switch
  [517,991]
];

green_dragon_coords = [511.5,594];
green_dragon_angle = -20;

snack_hole_coords = [39.5,703.5];
arcade_hole_coords = [266,873.5];
millionloop_hole_coords = [135,942];
hidden_entrance_hole_coords = [340.2,1116.3];
drain_hole_coords = [303,53.5];

arcade_target_coords = [278, 807];
arcade_target_angle = -20;//degrees

HIT_targets_coords = [311, 626];
HIT_target_angle = -71.5;

popbumpers_list = [
["royalblue",   388.8, 631.7],
["lightseagreen", 437.3, 690.0],
["orangered",  543.0, 670.0]
];

right_slingshot_coords=[300 + 172, 351];
right_slingshot_angle=71.5;

left_slingshot_coords=[300 - 172, 351];
left_slingshot_angle=-71.5;

flipper_coords = [
  [-65, 62.5, 746.5], //top left
  [-35, 197.5, 181.5], //bottom left
  [180+35, 402, 181.5] //bottom right
];

screws_coords = [
	[ 9.84506238588, 96.3401742322 ],
	[ 70.848607577, 144.379295746 ],
	[ 9.84506238588, 195.540504232 ],
	[ 108.595532577, 97.5396092322 ],
	[ 44.875618757, 197.083880746 ],
	[ 9.84506238588, 221.354800746 ],
	[ 31.373085077, 256.623495482 ],
	[ 9.84506238588, 296.46942891 ],
	[ 31.478917577, 316.383549438 ],
	[ 9.84506238588, 370.83439891 ],
	[ 42.785497437, 370.957841938 ],
	[ 9.84506238588, 663.408316938 ],
	[ 65.530453897, 671.028256938 ],
	[ 36.391238897, 709.877617924 ],
	[ 9.84506238588, 753.886299174 ],
	[ 573.473692868, 470.502141674 ],
	[ 605.523160508, 530.64147391 ],
	[ 628.356734048, 544.496697924 ],
	[ 610.911869188, 470.713806674 ],
	[ 627.359933008, 473.59775641 ],
	[ 407.263610508, 5.86219114954 ],
	[ 256.231870077, 6.84996114954 ],
	[ 532.348876688, 45.0996054822 ],
	[ 545.498635368, 6.24143838554 ],
	[ 576.101725508, 6.33843739954 ],
	[ 562.264196688, 26.3936961495 ],
	[ 534.359694188, 82.8641692322 ],
	[ 627.518822868, 112.100383246 ],
	[ 588.589960508, 125.435278246 ],
	[ 626.054665508, 6.77060088554 ],
	[ 594.455056548, 397.442439174 ],
	[ 627.801042868, 449.811887924 ],
	[ 594.128528008, 451.752150424 ],
	[ 612.975744048, 397.68057641 ],
	[ 612.719770508, 132.958219232 ],
	[ 584.938880368, 148.224543246 ],
	[ 628.559368008, 269.931918246 ],
	[ 627.554100368, 376.822740424 ],
	[ 605.611495368, 378.921751674 ],
	[ 627.324655508, 135.709864232 ],
	[ 9.84506238588, 5.86219114954 ],
	[ 206.102542577, 33.9519154822 ],
	[ 155.276413897, 58.0817254822 ],
	[ 9.84506238588, 76.6729679822 ],
	[ 228.098204937, 6.72648989954 ],
	[ 118.138237437, 6.84996114954 ],
	[ 9.84506238588, 397.389522924 ],
	[ 59.507032437, 428.857052924 ],
	[ 39.098857577, 464.628437924 ],
	[ 9.84506238588, 516.265864438 ],
	[ 33.807232577, 627.636931938 ],
	[ 9.84506238588, 643.00031141 ],
	[ 105.888670247, 788.130648198 ],
	[ 494.57545123, 788.154693342 ],
];

posts_coords = [
  [617.305 ,342.080],
  [410.4616, 200.030],
  [215.2479, 60.473],
  [1501.7811, 342.080],
  [1708.6245, 200.030],
  [1903.8382, 60.474]
];

module ball_hole(radius=15, $fn=30){
//TODO: move this to a library module
  epsilon=0.1;
  h=7;

  translate([0,0,-playfield_thickness-epsilon]){
    translate([0,0,h])
    cylinder(r1=radius, r2=radius+7, h=playfield_thickness - h + 2*epsilon);

    cylinder(r=radius, h=playfield_thickness + 2*epsilon);
  }
}

wood_color="BurlyWood";//[0.7, 0.7, 0.2];
dark_wood_color="Tan";
acrylic_color=[0.5, 0.5, 0.7, 0.8];

module vertical_upkicker(){
//TODO: implement this properly in an external library module
  ball_hole();
}

module drain(){
//TODO: implement this properly in an external library module
  ball_hole();
}

module green_dragon_hole2d(){
  width=38;
  height=42;
  square([width, height], center=true);

  translate([0, height/2])
  circle(r=8);
}

module green_dragon_hole(){
  translate([0,0,-playfield_thickness-1])
  linear_extrude(height=playfield_thickness+2)
  green_dragon_hole2d();
}

module wiring_hole(r=3, w=5, $fn=30){
  translate([0,0,-playfield_thickness-1])
  linear_extrude(height=playfield_thickness+2)
  hull(){
    translate([-w/2,0])
    circle(r=r);

    translate([w/2,0])
    circle(r=r);
  }
}

module playfield_wood_outline(PF_width=636.5, PF_length=1203.8){
  bottom_cut = [56,92];

  difference(){
    square([PF_width, PF_length]);

    translate([PF_width - bottom_cut[0], 0])
    square(bottom_cut);

    square(bottom_cut);
  }
}

//!playfield_wood(true);
module playfield_wood(render_from_source=false){
  color(wood_color)
  if (!render_from_source){
    import_stl("playfield_wood.stl");
  } else {
    render(){
      difference(){
        //This is the playfield wood border outline only.
        //All mounting holes are generated from the 
        //instantiation and placement of models of pinball parts.
       	translate([0,0,-playfield_thickness])
      	linear_extrude(height=playfield_thickness)
        playfield_wood_outline();

        //These are holes cut through the playfield wood.
        //The holes are a bit smaller than the actual lasercut acrylic inserts
        //so that the inserts will be placed on top of these supports.
        //TODO: we should generate these smaller shapes algorithmically,
        // maybe using somthing like the minkowski operator.
       	translate([0,0,-playfield_thickness-1])
      	linear_extrude(height=playfield_thickness+2)
	      import("playfield_insert_support.dxf", layer="support");

        //These are the actual insert holes.
        // TODO: we should restrict the level of detail in these
        // curves considering the diameter of the mills
        // used for CNC milling the playfield. Since laser cutting (for
        // the acrylic inserts) and CNC milling (for the inser holes) have 
        // different detail levels, we should restrict the shapes to the detail 
        // level that both technologies can produce, thus, making the acrylic 
        // parts fit nicely in the carved wood holes. 
       	translate([0,0,-insert_thickness])
      	linear_extrude(height=insert_thickness+1)
	      import("playfield_inserts.dxf", layer="inserts");

        //here are the mount holes for specific parts 

        translate(shooter_coords)
        shooter_cuts();

        translate(arcade_target_coords)
        rotate([0,0,arcade_target_angle])
        standup_mount_holes(playfield_thickness);

        translate(HIT_targets_coords)
        rotate([0,0,HIT_target_angle])
        standup_bank3_mount_holes(playfield_thickness);

        for (pop = popbumpers_list){
          translate([pop[1],pop[2]])
          popbumper_mount_holes(playfield_thickness);
        }

        translate(left_slingshot_coords)
        slingshot_mount_holes(angle=left_slingshot_angle);

        translate(right_slingshot_coords)
        slingshot_mount_holes(angle=right_slingshot_angle);

        for (coords = [snack_hole_coords, arcade_hole_coords, millionloop_hole_coords, hidden_entrance_hole_coords]){
          translate(coords)
          ball_hole();
        }

        translate(snack_hole_coords)
        vertical_upkicker();

        translate(drain_hole_coords)
        drain();

        for (f = flipper_coords){
          translate([f[1],f[2]])
          flipper_mount_holes();
        }

        for (r = rollover_coords){
          translate([r[1],r[2]])
          rotate([0,0,r[0]])
          rollover_mount_holes();
        }

        for (w = wiring_hole_coords){
          translate(w)
          wiring_hole();
        }

        translate(green_dragon_coords)
        rotate([0,0,green_dragon_angle])
        green_dragon_hole();

        //TODO: 
        //mount holes for:
        // - DropZone
      }
    }
  }
}

module playfield_borders(PF_width=636.5, PF_length=1185.8, width=18, height=25){
  bottom_cut = 92;

  color(dark_wood_color){
    //top border
    translate([0, PF_length])
    cube([PF_width , width, height]);

    //left border
    translate([0,bottom_cut])
    cube([width, PF_length - bottom_cut, height]);

    //right border
    translate([PF_width - width, bottom_cut])
    cube([width, PF_length - bottom_cut, height]);
  }
}

module playfield_inserts(){
    color(acrylic_color)
    translate([0,0,- insert_thickness])
  	linear_extrude(height=insert_thickness)
	  import("playfield_inserts.dxf");
}

module tracks_left(){
  translate([0,0,left_tracks_height - link_radius + thickness])
  linear_extrude(height=thickness)
  import("track-outlines.dxf", layer="left-low");

  translate([0,0,left_tracks_height])
  linear_extrude(height =thickness)
  import("track-outlines.dxf", layer="left-high");

  if(links){
    color([1,0,0])
    translate([0,0,left_tracks_height])
    linear_extrude(height=thickness)
    import("track-outlines.dxf", layer="left-links");
  } else {
    track_left_links();
  }
}

module posts(){
  for (coords = posts_coords){

    translate([1.7593535*0.28222, 3212.8627*0.28222])
    translate([coords[0]*0.28222, -coords[1]*0.28222])
    post();
  }
}

post_color=[1,0.4,0.4];
module screws(){
  color(post_color)
  for (coords = screws_coords){
    translate([0, 1200])
    mirror([0,1,0]) translate(coords)
    translate([0,0,-0.7*inch]) /*TODO: figure out correct z positioning of wood studs */
    wood_thread_stud();
  }
}

exporting=false;
module acrylics(name, height, color=acrylic_color, cache=true){
  color(color){
    if (exporting){
      //when we're exporting the STLs we keep things at z=0 so we can freely reposition them later when loading form disk cache
      linear_extrude(height=acrylic_thickness)
      import("acrylics_outlines.dxf", layer=name);
    } else {
      translate([0,0,height])
      if (cache){
        import(str("cache/acrylic-", name, ".stl"));
      } else {
        linear_extrude(height=acrylic_thickness)
        import("acrylics_outlines.dxf", layer=name);
      }
    }
  }
}

module acrylic(){
  acrylics("level1", spacer_height);
  acrylics("level2a", spacer_height*2 + acrylic_thickness, color="salmon");
  acrylics("level2b", spacer_height*2 + acrylic_thickness, color="salmon");

  acrylics("center-level1a", spacer_height);
  acrylics("center-level1b", spacer_height);
  acrylics("center-level2a", spacer_height*2 + acrylic_thickness, color="salmon");
  acrylics("center-level2b", spacer_height*2 + acrylic_thickness, color="salmon");
}

module lane_guide_assembly(){
  for (y=[-1,1]){
    translate([0,y*16])
    post();
  }
  rotate([0,0,90])
  translate([0,0,12])
  lane_guide();
}

module lane_guides(){
  for (lg = lane_guide_coords){
    translate(lg)
    lane_guide_assembly();
  }

//  translate([340, 1040]) ball();
  translate([372, 900]) ball();
}

module track_left_links(){
  translate([0,0,left_tracks_height + thickness]){

	  translate([45, 370])
    rotate([0,0,60])
	  link(24);

	  translate([39, 398])
	  link();

	  translate([39, 468])
	  link();

	  translate([39, 629])
	  link();
  }
}

module tracks_right(){
  translate([0,0,right_tracks_height - link_radius + thickness])
  linear_extrude(height=thickness)
  import("track-outlines.dxf", layer="right-low");

  translate([0,0,right_tracks_height])
  linear_extrude(height =thickness)
  import("track-outlines.dxf", layer="right-high");

  if(links){
    color([1,0,0])
    translate([0,0,right_tracks_height])
    linear_extrude(height=thickness)
    import("track-outlines.dxf", layer="right-links");
  } else {
    translate([18.5,108])
    track_right_links();
  }
}

module link_shape(radius=link_radius){
	difference(){
		circle(r=radius);
		circle(r=radius-link_thickness);
		translate([-radius,0]) square([2*radius, radius]);
	}
}

module link(r=link_radius){
	rotate([90,0,0])
	linear_extrude(height=link_width)
	link_shape(r);
}

module track_right_links(){
  translate([0,0,right_tracks_height + thickness]){

	  translate([536, 262])
    rotate([0,0,-60])
	  link(24);

	  translate([541.5, 330])
	  link();

	  translate([541.5, 493])
	  link();

	  translate([504, 595])
	  link();

	  translate([504, 682])
	  link();

	  translate([461, 565])
	  rotate([0,0,61.5])
	  link(23);

	  translate([367, 612])
	  rotate([0,0,63])
	  link(23);

	  translate([347, 650])
	  link(24);

	  translate([347, 715])
	  link(24);

	  translate([347, 800])
	  link(24);

  }
}

module flippers(){
  for (f=flipper_coords){
    translate([f[1],f[2]])
    flipper(angle=f[0], rubber_color="red");
  }
}

module popbumpers(){
  for (pop = popbumpers_list){
    translate([pop[1],pop[2]])
    popbumper(game="partyland", 
version="msdos", cap_color=pop[0]);
  }
}

module ball(){
  //the ball depicted in the game seems to have a diameter of 1+1/8 inch
  r=(1+1/8)*inch/2;
  
  color("silver")
  translate([0,0,r])
  sphere(r=r, $fn=20);
}

module balls(){
  for (coords = ball_list){
    translate(coords)
    ball();
  }

  translate([0,0,-25]){
    for (coords = [snack_hole_coords, arcade_hole_coords, millionloop_hole_coords]){
      translate(coords)
      ball();
    }
  }
}

module arcade_target(){
  translate(arcade_target_coords)
  rotate([0,0,arcade_target_angle])
  standup();
}

module HIT_targets(){
  translate(HIT_targets_coords)
  rotate([0,0,HIT_target_angle])
  standup_bank3(color="orangered");
}

module slingshots(){
    translate(left_slingshot_coords)
    slingshot(angle=left_slingshot_angle);

    translate(right_slingshot_coords)
    slingshot(angle=right_slingshot_angle);
}

module puke_inserts(){
  color(acrylic_color)
  linear_extrude(height=insert_thickness)
  import("puke_inserts_outline.dxf", layer="puke");
}

module upper_playfield_wood(cache=true){
  color(wood_color){
    if (cache){
      import("puke_playfield.stl");
    } else {
      render(){
        difference(){
          linear_extrude(height=playfield_thickness)
          import("acrylics_outlines.dxf", layer="center-level3b");

          translate([0, 0, playfield_thickness - insert_thickness])
          linear_extrude(height=insert_thickness+1)
          import("puke_inserts_outline.dxf", layer="puke");

          translate([0,0,-1])
          linear_extrude(height=playfield_thickness+2)
          import("puke_inserts_outline.dxf", layer="puke_support");

          for (coords = puke_rollover_coords){
            translate(coords)
            rollover_mount_holes();
          }
        }
      }
    }
  }
}

//!translate([320,1000]) rotate([0, 360*$t, 180+360*$t]) translate([-320,-1000])
// upper_playfield_wood();
module upper_playfield_assembly(){
  
  if(false){ /*TODO: caching*/
    color("steelblue") import("cache/acrylic-center-level3.stl");
    color(acrylic_color) import("cache/acrylic-center-level3.5.stl");
    color("magenta") import("cache/acrylic-center-level4.stl");
  } else {
    upper_playfield_wood();

    raise(playfield_thickness - insert_thickness)
    puke_inserts();

    for (coords = puke_rollover_coords){
      translate(coords)
      rollover();
    }

    acrylics("center-level3.5", playfield_thickness);
    acrylics("center-level4", playfield_thickness + spacer_height, color="magenta");
    acrylics("center-level4", playfield_thickness + 2*spacer_height + acrylic_thickness, color="magenta");
  }

  translate([0,0,playfield_thickness])
  lane_guides();

}

module skyride(){
  color("grey")
  linear_extrude(height=81)
  import("ramps_outlines.dxf", layer="skyride");

#  translate([201,1000])
  linear_extrude(height=30)
  rotate([0,0,90])
  rotate([90,0,0])
  import("ramp_level.dxf");

}

module cyclone(){
  //TODO
}

module ramps(){
  skyride_ramp(max_height=27);
  cyclone();
}

module rollovers(){
  for (r = rollover_coords){
    translate([r[1],r[2], -playfield_thickness])
    rotate([0,0,r[0]])
    rollover();
  }
}

module PARTYLand_Playfield(){
  playfield_wood();
  playfield_borders();

  playfield_inserts();

  ramps();
  tracks_left();
  //posts();
  //screws();
  acrylic();
  flippers();
  slingshots();
  rollovers();
  popbumpers();
  balls();
  arcade_target();
  HIT_targets();

  translate([0,0,PUKE_height]){
    upper_playfield_assembly();
    tracks_right();
  }
}


/*
//animation
rotate([0,360*$t -120,0])
translate([0,0,300])
rotate([0, 90, 0])
PARTYLand_Playfield();
*/

PARTYLand_Playfield();
